package com.ybkj.daijia.api.driver.v4;

import com.ybkj.daijia.Setting;
import com.ybkj.daijia.serverUtils.SettingUtils;
import com.ybkj.daijia.api.beatpay.utils.BestPayCreateOrder;
import com.ybkj.daijia.api.beatpay.utils.BestPayOrderModel;
import com.ybkj.daijia.api.beatpay.utils.CryptTool;
import com.ybkj.daijia.api.driver.transport.ResultTransport;
import com.ybkj.daijia.api.member.alipay.config.AlipayConfig;
import com.ybkj.daijia.api.member.alipay.sign.DriverOrderSign;
import com.ybkj.daijia.api.member.alipay.sign.RSA;
import com.ybkj.daijia.api.member.alipay.util.AlipayNotify;
import com.ybkj.daijia.api.unionpay.AcpService;
import com.ybkj.daijia.api.unionpay.DemoBase;
import com.ybkj.daijia.api.unionpay.LogUtil;
import com.ybkj.daijia.api.unionpay.SDKConfig;
import com.ybkj.daijia.api.unionpay.SDKConstants;
import com.ybkj.daijia.api.util.ErrorConstantV4;
import com.ybkj.daijia.api.weixin.tenpayv3.Configuration;
import com.ybkj.daijia.api.weixin.tenpayv3.GetWxOrderno;
import com.ybkj.daijia.api.weixin.tenpayv3.RequestHandler;
import com.ybkj.daijia.api.weixin.tenpayv3.Sha1Util;
import com.ybkj.daijia.api.weixin.tenpayv3.TenpayUtil;
import com.ybkj.daijia.server.event.model.DriverRechargeEvent;
import com.ybkj.daijia.server.model.Driver;
import com.ybkj.daijia.server.model.DriverEnchashment;
import com.ybkj.daijia.server.model.DriverLogin;
import com.ybkj.daijia.server.model.PayRecordDriver;
import com.ybkj.daijia.server.model.PayRecordDriver.PayRecordTypeDriver;
import com.ybkj.daijia.server.reactor.ReactorVo;
import com.ybkj.daijia.server.service.DriverLoginService;
import com.ybkj.daijia.server.service.DriverService;
import com.ybkj.daijia.server.service.PayRecordDriverService;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 支付相关
 *
 * @author mqs
 */
@Controller("PayControllerV4")
@RequestMapping(value = "driver/api/rest/v4/pay")
public class PayController {

    public static final String HELP_PAY_PROFIX = "HP";
    private static final Logger logger = LoggerFactory.getLogger(PayController.class);
    @Autowired
    private PayRecordDriverService payRecordDriverService;
    @Autowired
    private DriverService driverService;
    @Autowired
    private DriverLoginService driverLoginService;
    @Autowired
    private SettingUtils settingUtils;
    @Autowired
    private ApplicationEventMulticaster applicationEventMulticaster;

    public static Map<String, String> getAllRequestParam(final HttpServletRequest request) {
        Map<String, String> res = new HashMap<String, String>();
        Enumeration<?> temp = request.getParameterNames();
        if (null != temp) {
            while (temp.hasMoreElements()) {
                String en = (String) temp.nextElement();
                String value = request.getParameter(en);
                res.put(en, value);
                if (null == res.get(en) || "".equals(res.get(en))) {
                    res.remove(en);
                }
            }
        }
        return res;
    }

    /***
     * $$$ 司机支付宝充值
     * @param response
     * @param request
     * @param employToken
     * @param deviceType
     * @param money
     * @param type
     * @return
     */
    @RequestMapping(value = "alipay/prepay")
    public @ResponseBody
    ResultTransport alipayPrepay(HttpServletResponse response, HttpServletRequest request,
        String employToken, String deviceType, BigDecimal money,
        @RequestParam(value = "type", required = false) String type) {

        if (StringUtils.isBlank(employToken) || StringUtils.isBlank(deviceType) || null == money) {
            return ResultTransport.getErrorBy(ErrorConstantV4.PARAM_ERROR);
        }

        DriverLogin driverLogin = driverLoginService.selectByToken(employToken);

        if (null == driverLogin) {
            return ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_ERROR);
        }

        if (System.currentTimeMillis() >= driverLogin.getDeathDate().getTime()) {
            return ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_TIME_ERROR);
        }

        Driver driver = driverService.findOne(driverLogin.getDriverId());

        if (null == driver) {
            return ResultTransport.getErrorBy(ErrorConstantV4.EMPLOY_NOT_EXISTS);
        }

        try {

            Date now = new Date();
            SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            String currTime = outFormat.format(now);
            String strTime = currTime.substring(8, currTime.length());
            String strRandom = TenpayUtil.buildRandom(8) + "";
            String tradeNO = strTime + strRandom;

            //如果是余额就不变, 是可代付,就+ HP
            if (null == type || type.equals(DriverEnchashment.HELP_PAY)) {
                tradeNO = HELP_PAY_PROFIX + tradeNO;
            }
            logger
                .info("recv employToken:{},deviceType:{},money:{},type:{},tradeNO:{}", employToken,
                    deviceType, money, type, tradeNO);
            PayRecordDriver payRecordDriver = new PayRecordDriver();
            payRecordDriver.setAmount(money);
            payRecordDriver.setDeviceType(deviceType);
            payRecordDriver.setDriverId(driver.getId());
            payRecordDriver.setPhone(driver.getPhone());
            payRecordDriver.setTradeNO(tradeNO);
            payRecordDriver.setPayRecordType(PayRecordTypeDriver.alipay);

            payRecordDriverService.insertRecord(payRecordDriver);

            ResultTransport instance = ResultTransport.getErrorBy(ErrorConstantV4.SUCCESS);

            Setting setting = settingUtils.get();

            Map<String, Object> map = new HashMap<String, Object>();
            map.put("PartnerID", setting.getAlipayPartnerID());
            map.put("SellerID", setting.getAlipaySellerID());
            map.put("PartnerPrivKey", setting.getAlipayPartnerPrivKey());
//			map.put("PartnerPublicKey", setting.getAlipayPartnerPublicKey());
            map.put("PartnerPublicKey", AlipayConfig.ali_public_key);
            map.put("tradeNO", payRecordDriver.getTradeNO());
            map.put("productName", "服务人员充值");
            map.put("productDescription", "服务人员充值");
//			map.put("amount", 0.1);
            map.put("amount", money);
            map.put("notifyURL", setting.getWebUrl() + "/driver/api/rest/v3/pay/alipay/notify");

            String content = DriverOrderSign.sign(map);
            String sign = RSA.sign(content, setting.getAlipayPartnerPrivKey(), "utf-8");
            sign = URLEncoder.encode(sign, "utf-8");
            String url = content + "&sign=\"" + sign + "\"&sign_type=\"RSA\"";

            instance.setData(url);

            return instance;

        } catch (Exception e) {
            logger.error("api v3 Driver pay alipay/prepay error: {}", e);
            return ResultTransport.getErrorBy(ErrorConstantV4.SERVER_ERROR);
        }
    }

    @RequestMapping(value = "alipay/notify")
    public synchronized @ResponseBody
    String alipayNotify(HttpServletResponse response, HttpServletRequest request)
        throws UnsupportedEncodingException {
        logger.debug("alipay/notify---------------");

        Setting setting = settingUtils.get();

        //获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<String, String>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                    : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
            params.put(name, valueStr);
        }

        //获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//

        //商户订单号
        String out_trade_no = new String(
            request.getParameter("out_trade_no").getBytes("ISO-8859-1"), "UTF-8");

        //支付宝交易号
        String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),
            "UTF-8");

        //交易状态
        String trade_status = new String(
            request.getParameter("trade_status").getBytes("ISO-8859-1"), "UTF-8");

        //获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//

        logger.debug("商户订单号：" + out_trade_no);
        logger.debug("交易号：" + trade_no);
        logger.debug("交易状态：" + trade_status);

        if (AlipayNotify.verify(params, setting)) {//验证成功
            //请在这里加上商户的业务逻辑程序代码

            if (trade_status.equals("TRADE_FINISHED")) {
                //判断该笔订单是否在商户网站中已经做过处理
                //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                //如果有做过处理，不执行商户的业务程序

                //注意：
                //该种交易状态只在两种情况下出现
                //1、开通了普通即时到账，买家付款成功后。
                //2、开通了高级即时到账，从该笔交易成功时间算起，过了签约时的可退款时限（如：三个月以内可退款、一年以内可退款等）后。
                PayRecordDriver payRecordDriver = payRecordDriverService
                    .findByTradeNO(out_trade_no);
                if (null != payRecordDriver && !payRecordDriver.isTreatment()) {

                    try {
                        driverService.cashRecharge(payRecordDriver, trade_no);

                        Driver driver = driverService.findOne(payRecordDriver.getDriverId());
                        if (null != driver) {
                            ReactorVo rVo = new ReactorVo();
                            rVo.setDriver(driver);
                            rVo.setMoney(payRecordDriver.getAmount());
                            rVo.setType("alipay");
                            DriverRechargeEvent rechargeEvent = new DriverRechargeEvent(rVo);
                            applicationEventMulticaster.multicastEvent(rechargeEvent);
                        }

                        logger.debug("success-----------------");
                        return "success";
                    } catch (Exception e) {
                        logger.debug("fail：" + e.getMessage());
                    }

                } else {
                    logger.debug("订单：" + out_trade_no + "不存在或已完成");
                }


            } else if (trade_status.equals("TRADE_SUCCESS")) {
                //判断该笔订单是否在商户网站中已经做过处理
                //如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
                //如果有做过处理，不执行商户的业务程序

                //注意：
                //该种交易状态只在一种情况下出现——开通了高级即时到账，买家付款成功后。
                PayRecordDriver payRecordDriver = payRecordDriverService
                    .findByTradeNO(out_trade_no);
                if (null != payRecordDriver && !payRecordDriver.isTreatment()) {
                    try {
                        driverService.cashRecharge(payRecordDriver, trade_no);

                        Driver driver = driverService.findOne(payRecordDriver.getDriverId());
                        if (null != driver) {
                            ReactorVo rVo = new ReactorVo();
                            rVo.setDriver(driver);
                            rVo.setMoney(payRecordDriver.getAmount());
                            rVo.setType("alipay");
                            DriverRechargeEvent rechargeEvent = new DriverRechargeEvent(rVo);
                            applicationEventMulticaster.multicastEvent(rechargeEvent);
                        }

                        logger.debug("success-----------------");
                        return "success";
                    } catch (Exception e) {
                        logger.debug("fail：" + e.getMessage());
                    }
                } else {
                    logger.debug("订单：" + out_trade_no + "不存在或已完成");
                }
            }

            logger.debug("fail-----------------");
            return "fail";

        } else {
            logger.debug("验证失败-----------------");
            return "fail";
        }
    }

    /***
     * $$$ 微信支付生成订单号, 司机微信充值
     * @param response
     * @param request
     * @param employToken
     * @param deviceType
     * @param money
     * @param type
     * @return
     */
    @RequestMapping(value = "wx/prepay")
    public synchronized @ResponseBody
    ResultTransport wxAfterpay(
        HttpServletResponse response, HttpServletRequest request,
        String employToken, String deviceType, BigDecimal money,
        @RequestParam(value = "type", required = false) String type) {

//		logger.debug("recv employToken:{},deviceType:{},money:{}",employToken,deviceType,money);

        if (StringUtils.isBlank(employToken) || StringUtils.isBlank(deviceType) || null == money) {
            return ResultTransport.getErrorBy(ErrorConstantV4.PARAM_ERROR);
        }

        DriverLogin driverLogin = driverLoginService.selectByToken(employToken);

        if (null == driverLogin) {
            return ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_ERROR);
        }

        if (System.currentTimeMillis() >= driverLogin.getDeathDate().getTime()) {
            return ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_TIME_ERROR);
        }

        Driver driver = driverService.findOne(driverLogin.getDriverId());

        if (null == driver) {
            return ResultTransport.getErrorBy(ErrorConstantV4.EMPLOY_NOT_EXISTS);
        }

        //获取openId后调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder
        String currTime = TenpayUtil.getCurrTime();
        //8位日期
        String strTime = currTime.substring(8, currTime.length());
        //四位随机数
        String strRandom = TenpayUtil.buildRandom(6) + "";
        //10位序列号,可以自行调整。

//		if(null != type){
//			if(type.equals(DriverEnchashment.HELP_PAY)){
//				strTime = HELP_PAY_PROFIX + strTime;
//			}
//		}

        //如果是余额就不变, 是可代付,就+ HP
        String tradeNO = strTime + strRandom;
        if (null == type || type.equals(DriverEnchashment.HELP_PAY)) {
            tradeNO = HELP_PAY_PROFIX + tradeNO;
        }
        logger.info("recv employToken:{},deviceType:{},money:{}, tradeNO:{},type{}", employToken,
            deviceType, money, tradeNO, type);

        PayRecordDriver payRecordDriver = new PayRecordDriver();
        payRecordDriver.setAmount(money);
        payRecordDriver.setDeviceType(deviceType);
        payRecordDriver.setDriverId(driver.getId());
        payRecordDriver.setPhone(driver.getPhone());
        payRecordDriver.setTradeNO(tradeNO);
        payRecordDriver.setPayRecordType(PayRecordTypeDriver.wx);

        payRecordDriverService.insertRecord(payRecordDriver);

        ResultTransport instance = ResultTransport.getErrorBy(ErrorConstantV4.SUCCESS);

        Setting setting = settingUtils.get();
        String createOrderURL = Configuration.createOrderURL;

        //商户相关资料
        String appid = setting.getWxAppIDEmploy();
        String appsecret = setting.getWxAppSecretEmploy();
        String partner = setting.getWxPartnerIDEmploy();
        String partnerkey = setting.getWxPartnerKeyEmploy();

        //商户号
        String mch_id = partner;
        //随机数
        String nonce_str = tradeNO;
        //商品描述
        String body = "服务人员充值";
        //商户订单号
        String out_trade_no = payRecordDriver.getTradeNO();
        //总金额以分为单位，不带小数点
        String total_fee = String.valueOf(money.multiply(new BigDecimal(100)).intValue());
        //订单生成的机器 IP
        String spbill_create_ip = request.getRemoteAddr();

        //这里notify_url是 支付完成后微信发给该链接信息，可以判断会员是否支付成功，改变订单状态等。
        String notify_url = setting.getWebUrl() + "/driver/api/rest/v4/pay/wx/notify";

        String trade_type = "APP";

        SortedMap<String, String> packageParams = new TreeMap<String, String>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", body);
        packageParams.put("out_trade_no", out_trade_no);

        //这里写的金额为1 分到时修改
//			packageParams.put("total_fee", "1");
        packageParams.put("total_fee", total_fee);
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("notify_url", notify_url);
        packageParams.put("trade_type", trade_type);

        RequestHandler reqHandler = new RequestHandler(request, response);
        reqHandler.init(appid, appsecret, partnerkey);

        String sign = reqHandler.createSign(packageParams);
        String xml = "<xml>" +
            "<appid>" + appid + "</appid>" +
            "<mch_id>" + mch_id + "</mch_id>" +
            "<nonce_str>" + nonce_str + "</nonce_str>" +
            "<sign>" + sign + "</sign>" +
            "<body><![CDATA[" + body + "]]></body>" +
            "<out_trade_no>" + out_trade_no + "</out_trade_no>" +
//					"<total_fee>"+1+"</total_fee>"+
            "<total_fee>" + total_fee + "</total_fee>" +
            "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>" +
            "<notify_url>" + notify_url + "</notify_url>" +
            "<trade_type>" + trade_type + "</trade_type>" +
            "</xml>";
        String prepay_id = "";
        try {
            prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml);
            if (prepay_id.equals("")) {
                logger.error("统一支付接口获取预支付订单出错");
            }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        SortedMap<String, String> finalpackage = new TreeMap<String, String>();
        String timestamp = Sha1Util.getTimeStamp();
        finalpackage.put("appid", appid);
        finalpackage.put("partnerid", partner);
        finalpackage.put("prepayid", prepay_id);
        finalpackage.put("package", "Sign=WXPay");
        finalpackage.put("noncestr", nonce_str);
        finalpackage.put("timestamp", timestamp);
        String finalsign = reqHandler.createSign(finalpackage);
        finalpackage.put("sign", finalsign);

        instance.setData(finalpackage);
        return instance;

    }

    //微信支付后接口
    @RequestMapping("wx/notify")
    public synchronized @ResponseBody
    String wxAfterNotify(HttpServletResponse response, HttpServletRequest request)
        throws Exception {

        String out_trade_no = "";

        String trade_no = "";

        try {

            BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
            String line = null;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            String strxml = sb.toString();

            InputStream in = new ByteArrayInputStream(strxml.getBytes());
            SAXBuilder builder = new SAXBuilder();
            Document doc = builder.build(in);
            Element root = doc.getRootElement();
            List list = root.getChildren();
            Iterator it = list.iterator();
            while (it.hasNext()) {
                Element e = (Element) it.next();
                String k = e.getName();
                if (k.equals("out_trade_no")) {
                    out_trade_no = e.getText();
                } else if (k.equals("transaction_id")) {
                    trade_no = e.getText();
                }
            }

            try {
                //关闭流
                if (null != in) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            if (StringUtils.isBlank(out_trade_no)) {
                return "FAIL";
            }

            PayRecordDriver payRecordDriver = payRecordDriverService.findByTradeNO(out_trade_no);

            if (null == payRecordDriver) {
                return "FAIL";
            }

            if (payRecordDriver.isTreatment()) {
                return "SUCCESS";
            }

            try {

                driverService.cashRecharge(payRecordDriver, trade_no);

                Driver driver = driverService.findOne(payRecordDriver.getDriverId());
                if (null != driver) {
                    ReactorVo rVo = new ReactorVo();
                    rVo.setDriver(driver);
                    rVo.setMoney(payRecordDriver.getAmount());
                    rVo.setType("weixin");
                    DriverRechargeEvent rechargeEvent = new DriverRechargeEvent(rVo);
                    applicationEventMulticaster.multicastEvent(rechargeEvent);
                }

                return "SUCCESS";


            } catch (Exception e) {
                e.printStackTrace();
                return "FAIL";
            }


        } catch (Exception e) {
            e.printStackTrace();
            return "FAIL";
        }

    }

    /**
     * 银联支付接口.
     */
    @RequestMapping(value = "unionpay/prepay")
    public @ResponseBody
    ResultTransport unionpay(HttpServletResponse response, HttpServletRequest request,
        String employToken, String deviceType, BigDecimal money) {

        logger.debug("recv employToken:{},deviceType:{},money:{}", employToken, deviceType, money);

        if (StringUtils.isBlank(employToken) || StringUtils.isBlank(deviceType) || null == money) {
            return ResultTransport.getErrorBy(ErrorConstantV4.PARAM_ERROR);
        }

        DriverLogin driverLogin = driverLoginService.selectByToken(employToken);

        if (null == driverLogin) {
            return ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_ERROR);
        }

        if (System.currentTimeMillis() >= driverLogin.getDeathDate().getTime()) {
            return ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_TIME_ERROR);
        }

        Driver driver = driverService.findOne(driverLogin.getDriverId());

        if (null == driver) {
            return ResultTransport.getErrorBy(ErrorConstantV4.EMPLOY_NOT_EXISTS);
        }

        try {

            String orderId = DemoBase.getOrderId();

            PayRecordDriver payRecordDriver = new PayRecordDriver();
            payRecordDriver.setAmount(money);
            payRecordDriver.setDeviceType(deviceType);
            payRecordDriver.setDriverId(driver.getId());
            payRecordDriver.setPhone(driver.getPhone());
            payRecordDriver.setTradeNO(orderId);
            payRecordDriver.setPayRecordType(PayRecordTypeDriver.unionpay);

            payRecordDriverService.insertRecord(payRecordDriver);

            Setting setting = settingUtils.get();
            // 从classpath加载acp_sdk.properties文件
            SDKConfig.getConfig().loadPropertiesFromSrc(setting);

            int payMoney = money.multiply(new BigDecimal(100))
                .intValue();  //txnAmt以'元'为单位,乘以100以'分'为单位传给银联
            String txnTime = DemoBase.getCurrentTime(); //交易时间
            String merId = setting.getMerId();  //获取商户号
            Map<String, String> contentData = new HashMap<String, String>();
            /***银联全渠道系统，产品参数，除了encoding自行选择外其他不需修改***/
            contentData.put("version", "5.1.0");            //版本号 全渠道默认值
            contentData.put("encoding", "UTF-8");     //字符集编码 可以使用UTF-8,GBK两种方式
            contentData.put("signMethod", "01"); //签名方法
            contentData.put("txnType", "01");                       //交易类型 01:消费
            contentData.put("txnSubType", "01");                    //交易子类 01：消费
            contentData.put("bizType", "000201");                   //填写000201
            contentData.put("channelType", "08");                   //渠道类型 08手机

            /***商户接入参数***/
            contentData
                .put("merId", merId);                        //商户号码，请改成自己申请的商户号或者open上注册得来的777商户号测试
            contentData.put("accessType",
                "0");                     //接入类型，商户接入填0 ，不需修改（0：直连商户， 1： 收单机构 2：平台商户）
            contentData.put("orderId",
                orderId);                    //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则
            contentData.put("txnTime",
                txnTime);                    //订单发送时间，取系统时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
            contentData
                .put("accType", "01");                       //账号类型 01：银行卡02：存折03：IC卡帐号类型(卡介质)
            contentData
                .put("txnAmt", String.valueOf(payMoney));                      //交易金额 单位为分，不能带小数点
            contentData.put("currencyCode", "156");                 //境内商户固定 156 人民币

            contentData.put("reqReserved", orderId);
            String backUrl = "";
            if (setting.getWebUrl().endsWith("/")) {
                backUrl = setting.getWebUrl() + "driver/api/rest/v4/pay/unionpay/notify";
            } else {
                backUrl = setting.getWebUrl() + "/" + "driver/api/rest/v4/pay/unionpay/notify";
            }
            contentData.put("backUrl", backUrl);

            /**对请求参数进行签名并发送http post请求，接收同步应答报文**/
            Map<String, String> reqData = AcpService.sign(contentData,
                DemoBase.encoding);            //报文中certId,signature的值是在signData方法中获取并自动赋值的，只要证书配置正确即可。
            String requestAppUrl = SDKConfig.getConfig()
                .getAppRequestUrl();                                 //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
            Map<String, String> rspData = AcpService.post(reqData, requestAppUrl,
                DemoBase.encoding);  //发送请求报文并接受同步应答（默认连接超时时间30秒，读取返回结果超时时间30秒）;这里调用signData之后，调用submitUrl之前不能对submitFromData中的键值对做任何修改，如果修改会导致验签不通过

            if (!rspData.isEmpty()) {
                if (AcpService.validate(rspData, DemoBase.encoding)) {
                    LogUtil.writeLog("验证签名成功");
                    String respCode = rspData.get("respCode");
                    if (("00").equals(respCode)) {
                        String tn = rspData.get("tn");
                        ResultTransport transport = ResultTransport
                            .getErrorBy(ErrorConstantV4.SUCCESS);
                        transport.setData(tn);
                        return transport;
                    } else {
                        return ResultTransport.getErrorBy(ErrorConstantV4.SERVER_ERROR);
                    }
                } else {
                    LogUtil.writeErrorLog("验证签名失败");
                    return ResultTransport.getErrorBy(ErrorConstantV4.SERVER_ERROR);
                }
            } else {
                LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
                return ResultTransport.getErrorBy(ErrorConstantV4.SERVER_ERROR);
            }
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("api v3 Driver pay alipay/prepay error: {}", e);
            return ResultTransport.getErrorBy(ErrorConstantV4.SERVER_ERROR);
        }
    }

    /**
     * 银联支付回调.
     */
    @RequestMapping(value = "unionpay/notify")
    public synchronized @ResponseBody
    void unionpayNotify(
        HttpServletResponse resp, HttpServletRequest req) throws Exception {

        String encoding = req.getParameter(SDKConstants.param_encoding);
        Map<String, String> reqParam = getAllRequestParam(req);

        LogUtil.printRequestLog(reqParam);

        Map<String, String> valideData = null;
        if (null != reqParam && !reqParam.isEmpty()) {
            Iterator<Entry<String, String>> it = reqParam.entrySet().iterator();
            valideData = new HashMap<String, String>(reqParam.size());
            while (it.hasNext()) {
                Entry<String, String> e = it.next();
                String key = (String) e.getKey();
                String value = (String) e.getValue();

                valideData.put(key, value);
            }
        }
        if (!AcpService.validate(valideData, encoding)) {
            LogUtil.writeLog("验证签名结果[失败].");
        } else {
            LogUtil.writeLog("验证签名结果[成功].");
            String orderId = valideData.get("orderId");
            LogUtil.writeLog("后台通知的数据orderId:" + orderId);
            String respCode = valideData.get("respCode");

            if ("00".equals(respCode)) {

                String out_trade_no = valideData.get("reqReserved");  //获取后台通知的数据，其他字段也可用类似方式获取

                PayRecordDriver payRecordDriver = payRecordDriverService
                    .findByTradeNO(out_trade_no);

                if (null != payRecordDriver && !payRecordDriver.isTreatment()) {
                    try {
                        driverService.cashRecharge(payRecordDriver, out_trade_no);

                        Driver driver = driverService.findOne(payRecordDriver.getDriverId());
                        if (null != driver) {
                            ReactorVo rVo = new ReactorVo();
                            rVo.setDriver(driver);
                            rVo.setMoney(payRecordDriver.getAmount());
                            rVo.setType("unionpay");
                            DriverRechargeEvent rechargeEvent = new DriverRechargeEvent(rVo);
                            applicationEventMulticaster.multicastEvent(rechargeEvent);
                        }
                        //返回给银联服务器http 200  状态码
                        resp.getWriter().print("ok");
                    } catch (Exception e) {
                        logger.debug("fail：" + e.getMessage());
                    }
                } else {
                    logger.debug("订单：" + out_trade_no + "不存在或已完成");
                }

            }

        }
        LogUtil.writeLog("BackRcvResponse接收后台通知结束");
    }

    /****************************************************************************************************
     *
     * 翼支付
     * @throws Exception
     *
     ****************************************************************************************************/

    /**
     * 翼支付服务人员充值预支付
     *
     * @param request     请求对象
     * @param employToken token
     * @param deviceType  设备类型
     * @param money       充值金额
     * @return
     * @throws Exception
     */
    @ResponseBody
    @RequestMapping(value = "bestpay/prepay")
    public synchronized ResultTransport bestPayByPrepay(HttpServletRequest request,
        String employToken, String deviceType, BigDecimal money) throws Exception {

        logger
            .debug("recv employToken:{}, deviceType:{}, money:{}", employToken, deviceType, money);

        ResultTransport instance = null;

        // 验证参数
        if (StringUtils.isBlank(employToken)
            || StringUtils.isBlank(deviceType)
            || null == money) {
            instance = ResultTransport.getErrorBy(ErrorConstantV4.PARAM_ERROR);
            return instance;
        }

        // 获取司机
        DriverLogin driverLogin = driverLoginService.selectByToken(employToken);
        if (null == driverLogin) {
            instance = ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_ERROR);
            return instance;
        }

        if (System.currentTimeMillis() >= driverLogin.getDeathDate().getTime()) {
            instance = ResultTransport.getErrorBy(ErrorConstantV4.TOKEN_TIME_ERROR);
            return instance;
        }

        Driver driver = driverService.findOne(driverLogin.getDriverId());
        if (null == driver) {
            instance = ResultTransport.getErrorBy(ErrorConstantV4.EMPLOY_NOT_EXISTS);
            return instance;
        }

        // 格式化金额0.00
        DecimalFormat myformat = new java.text.DecimalFormat("0.00");
        String money_ = myformat.format(money);
        String phone = driver.getPhone();

        String currTime = TenpayUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length()); //8位日期
        String strRandom = TenpayUtil.buildRandom(6) + ""; //四位随机数
        String strReq = strTime + strRandom; // 10位序列号,可以自行调整

        // 插入支付记录
        PayRecordDriver payRecordDriver = new PayRecordDriver();
        payRecordDriver.setAmount(money);
        payRecordDriver.setDeviceType(deviceType);
        payRecordDriver.setDriverId(driver.getId());
        payRecordDriver.setPhone(phone);
        payRecordDriver.setTradeNO(strReq);
        payRecordDriver.setPayRecordType(PayRecordTypeDriver.bestpay);
        payRecordDriverService.insertRecord(payRecordDriver);

        // 获取系统设置
        Setting setting = settingUtils.get();
        String callBackUrl = setting.getWebUrl() + "/driver/api/rest/v4/pay/bestpay/notify";
        String createUrl = "https://webpaywg.bestpay.com.cn/order.action";

        // 获取商户信息
        String mch_id = setting.getMerchantId();
        String mch_key = setting.getMerKey();
        String mch_pwd = setting.getMchPwd();
        String riskInfo = setting.getRiskcontrolinfo(); // 风控信息
        String body = "服务人员充值";

        // 订单模型
        String total_fee = String.valueOf(money.multiply(new BigDecimal(100)).intValue()); // 元转分
        BestPayOrderModel orderModel = new BestPayOrderModel();
        orderModel.setATTACH("null");
        orderModel.setCITYCODE("");
        orderModel.setDIVDETAILS("");
        orderModel.setENCODE("");
        orderModel.setENCODETYPE("1");
        orderModel.setKEY(mch_key);
        orderModel.setLOGINNO("");
        orderModel.setMERCHANTID(mch_id);
        orderModel.setORDERAMT(total_fee); // 分
        orderModel.setORDERCCY("RMB");
        orderModel.setORDERREQTIME(currTime);
        orderModel.setORDERREQTRANSEQ(strReq);
        orderModel.setORDERSEQ(strReq);
        orderModel.setPRODUCTDESC(body);
        orderModel.setPRODUCTID("04");
        orderModel.setPROVINCECODE("");
        orderModel.setRISKCONTROLINFO(riskInfo);
        orderModel.setSERVICECODE("05");
        orderModel.setSESSIONKEY("");
        orderModel.setSUBMERCHANTID("");
        orderModel.setTRANSCODE("01");
        boolean rs = BestPayCreateOrder.createByApp(createUrl, orderModel);

        // 下单失败
        if (!rs) {
            instance = ResultTransport.getErrorBy(ErrorConstantV4.BESTPAY_APP_CREATE_ORDER_ERR);
            return instance;
        }

        // 签名参数
        StringBuffer md5Buffer = new StringBuffer();
        md5Buffer.append("SERVICE=").append("mobile.security.pay")
            .append("&MERCHANTID=").append(mch_id)
            .append("&MERCHANTPWD=").append(mch_pwd)
            .append("&SUBMERCHANTID=").append("")
            .append("&BACKMERCHANTURL=").append(callBackUrl)
            .append("&ORDERSEQ=").append(strReq)
            .append("&ORDERREQTRANSEQ=").append(strReq)
            .append("&ORDERTIME=").append(currTime)
            .append("&ORDERVALIDITYTIME=").append("")
            .append("&CURTYPE=").append("RMB")
            .append("&ORDERAMOUNT=").append(money_)
            .append("&SUBJECT=").append(body)
            .append("&PRODUCTID=").append("04")
            .append("&PRODUCTDESC=").append(body)
            .append("&CUSTOMERID=").append(phone)
            .append("&SWTICHACC=").append("true")
            .append("&KEY=").append(mch_key);

        // 签名
        logger.info("MAC(MD5)--before--String：" + md5Buffer.toString());
        String sign = CryptTool.md5Digest(md5Buffer.toString());
        logger.info("MAC(MD5)--after--String:" + sign);

        // 拼接返回数据串
        StringBuffer paramsBuffer = new StringBuffer();
        paramsBuffer.append("SERVICE=").append("mobile.security.pay")
            .append("&MERCHANTID=").append(mch_id) // 签约商户号
            .append("&MERCHANTPWD=").append(mch_pwd)
            .append("&SUBMERCHANTID=").append("")
            .append("&BACKMERCHANTURL=").append(callBackUrl)
            .append("&SIGNTYPE=").append("MD5")
            .append("&SIGN=").append(sign)
            .append("&ORDERSEQ=").append(strReq)
            .append("&ORDERREQTRANSEQ=").append(strReq)
            .append("&ORDERTIME=").append(currTime)
            .append("&ORDERVALIDITYTIME=").append("")
            .append("&ORDERAMOUNT=").append(money_)
            .append("&CURTYPE=").append("RMB")
            .append("&PRODUCTID=").append("04")
            .append("&PRODUCTDESC=").append(body)
            .append("&PRODUCTAMOUNT=").append(money_)
            .append("&ATTACHAMOUNT=").append("0.00")
            .append("&ATTACH=").append("")
            .append("&DIVDETAILS=").append("")
            .append("&ACCOUNTID=").append("")
            .append("&CUSTOMERID=").append(phone)
            .append("&USERIP=").append("")
            .append("&BUSITYPE=").append("04")
            .append("&SWTICHACC=").append("true")
            .append("&SUBJECT=").append(body);

        // 返回数据
        String params = paramsBuffer.toString();
        instance = ResultTransport.getErrorBy(ErrorConstantV4.SUCCESS);
        instance.setData(params);
        return instance;
    }

    /**
     * 翼支付服务人员充值预支付回调
     *
     * @param response
     * @param request
     * @return
     * @throws Exception
     */
    @RequestMapping("bestpay/notify")
    public synchronized void bestPayByPrepayNotify(HttpServletResponse response,
        HttpServletRequest request) throws Exception {
        try {
            // 准备
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();

            // 获取回调数据，注释部分为没有使用的部分数据，其所代表的意义详见翼支付APP文档
            String UPTRANSEQ = request.getParameter("UPTRANSEQ");
            String MERCHANTID = request.getParameter("MERCHANTID");
            String TRANDATE = request.getParameter("TRANDATE");
            String RETNCODE = request.getParameter("RETNCODE");
            String RETNINFO = request.getParameter("RETNINFO");
//			String ORDERREQTRANSEQ = request.getParameter("ORDERREQTRANSEQ");
            String ORDERSEQ = request.getParameter("ORDERSEQ");
            String ORDERAMOUNT = request.getParameter("ORDERAMOUNT");
//			String PRODUCTAMOUNT = request.getParameter("PRODUCTAMOUNT");
//			String ATTACHAMOUNT = request.getParameter("ATTACHAMOUNT");
//			String CURTYPE = request.getParameter("CURTYPE");
//			String ENCODETYPE = request.getParameter("ENCODETYPE");
//			String BANKID = request.getParameter("BANKID");
//			String ATTACH = request.getParameter("ATTACH");
//			String UPREQTRANSEQ = request.getParameter("UPREQTRANSEQ");
//			String UPBANKTRANSEQ = request.getParameter("UPBANKTRANSEQ");
//			String PRODUCTNO = request.getParameter("PRODUCTNO");
            String SIGN = request.getParameter("SIGN");

            // 验证签名
            Setting setting = settingUtils.get();
            String key = setting.getMerKey();
            String signStr = "UPTRANSEQ=" + UPTRANSEQ
                + "&MERCHANTID=" + MERCHANTID
                + "&ORDERSEQ=" + ORDERSEQ
                + "&ORDERAMOUNT=" + ORDERAMOUNT
                + "&RETNCODE=" + RETNCODE
                + "&RETNINFO=" + RETNINFO
                + "&TRANDATE=" + TRANDATE
                + "&KEY=" + key;
            String localSign = CryptTool.md5Digest(signStr);

            // 验签失败
            if (!localSign.equals(SIGN)) {
                out.write("UPTRANSEQ_" + UPTRANSEQ);
                logger.info("支付回调，验签失败！");
                return;
            }

            // 验签成功，看是否支付成功("0000为成功，其他均为失败")
            if (RETNCODE.equals("0000")) {
                PayRecordDriver payRecordDriver = payRecordDriverService.findByTradeNO(ORDERSEQ);

                if (null == payRecordDriver) {
                    out.write("UPTRANSEQ_" + UPTRANSEQ);
                    logger.info("司机充值临时记录不存在");
                    return;
                }

                if (payRecordDriver.isTreatment()) {
                    out.write("UPTRANSEQ_" + UPTRANSEQ);
                    logger.info("司机充值临时记录已完成");
                    return;
                }

                driverService.cashRecharge(payRecordDriver, UPTRANSEQ);
                Driver driver = driverService.findOne(payRecordDriver.getDriverId());
                if (null != driver) {
                    ReactorVo rVo = new ReactorVo();
                    rVo.setDriver(driver);
                    rVo.setMoney(payRecordDriver.getAmount());
                    rVo.setType("bestpay");
                    DriverRechargeEvent rechargeEvent = new DriverRechargeEvent(rVo);
                    applicationEventMulticaster.multicastEvent(rechargeEvent);
                }
            }
            out.write("UPTRANSEQ_" + UPTRANSEQ);
        } catch (Exception e) {
            // 异常不返回，以便不丢失支付数据
            e.printStackTrace();
        }
    }
}
